/*
AbacusSim.cpp: An educational assembly simulator, implementation file.
    Copyright (C) 2010  Daniel Gutson, FuDePAN

    This file is part of the learn-asm project.

    learn-asm is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    learn-asm is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with learn-asm.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <cstdlib>
#include <string>
#include <limits>
#include "AbacusSim.h"

class Register
{
    Value value;
    const std::string name;
public:
    Register(const std::string& name)
        : value(0), name(name)
    {}

    Value getValue() const    {   return value;   }
    void setValue(Value v)    {   value = v;      }

    friend std::ostream& operator << (std::ostream& o, const Register& r)
    {
        o << "Register " << r.name << " = " << r.value;
        return o;
    }
};

Value MemoryContent[MAX_MEM];
Register R1("R1"), R2("R2"), R3("R3"), R4("R4");
bool CR;
Memory memory;

struct MemoryAddressBuilder : public MemoryAddress
{
    MemoryAddressBuilder(int index)
        : MemoryAddress(static_cast<size_t>(index))
    {
        if (index < 0 || size_t(index) >= MAX_MEM)
            throw "Memory accessed out of bounds!";
    }
};


MemoryAddress::MemoryAddress(size_t idx) : index(idx) {  }
MemoryAddress::MemoryAddress(const MemoryAddress& other)   : index(other.index) { }

void MemoryAddress::setValue(Value v) const   {   MemoryContent[index] = v;       }
Value MemoryAddress::getValue() const         {   return MemoryContent[index];    }


MemoryAddress Memory::operator [] (const Immediate& imm) const
{
    return MemoryAddressBuilder(imm.value);
}

MemoryAddress Memory::operator [] (const Register& r) const
{
    return MemoryAddressBuilder(r.getValue());
}


// =====================================================

void MovImm(Register& r, const Immediate& imm)
{
    r.setValue(imm.value);
}

void MovRR(Register& to, const Register& from)
{
    to.setValue(from.getValue());
}

void Load(Register& r, const MemoryAddress& m)
{
    r.setValue(m.getValue());
}

void Store(const MemoryAddress& m, const Register& r)
{
    m.setValue(r.getValue());
}

void Compare(const Register& r1, const Register& r2)
{
    CR = r1.getValue() >= r2.getValue();
}

void Compare(const Register& r1, const Immediate& i)
{
    CR = r1.getValue() >= i.value;
}

// arithmetic:

// checks whether a sum will do wraparound or not:
static void _checkSumBoundaries(Value x, Value y)
{
    // check lower limit:
    if (y < 0)
    {
        if (x < (std::numeric_limits<Value>::min() - y))
            throw "Addition of a negative value will wrap around!";
    }
    else
    {
        if (x > (std::numeric_limits<Value>::max() - y))
            throw "Addition of a positive value will wrap around!";
    }
}

void Add(Register& r, const Register& rOperand)
{
    _checkSumBoundaries(r.getValue(), rOperand.getValue());
    r.setValue(r.getValue() + rOperand.getValue());
}

void Add(Register& r, const Immediate& iOperand)
{
    _checkSumBoundaries(r.getValue(), iOperand.value);
    r.setValue(r.getValue() + iOperand.value);
}

// checks whether a multiplication will do wraparound or not:
static void _checkMulBoundaries(Value x, Value y)
{
    const uint32_t ux = std::abs(x);
    const uint32_t uy = std::abs(y);
    if (uy > 0)
    {
        if (ux > (std::numeric_limits<Value>::max() / uy))
            throw "Multiplication will wrap around!";
    }
}

void Mul(Register& r, const Register& rOperand)
{
    _checkMulBoundaries(r.getValue(), rOperand.getValue());
    r.setValue(r.getValue() * rOperand.getValue());
}

void Mul(Register& r, const Immediate& iOperand)
{
    _checkMulBoundaries(r.getValue(), iOperand.value);
    r.setValue(r.getValue() * iOperand.value);
}

void Nand(Register& r, const Register& rOperand)
{
    r.setValue(~(r.getValue() & rOperand.getValue()));
}

void Nand(Register& r, const Immediate& iOperand)
{
    r.setValue(~(r.getValue() & iOperand.value));
}

// debug:
void PrintReg(const Register& r)
{
    std::cout << r << std::endl;
}

void PrintMem(const MemoryAddress& m)
{
    std::cout << m << std::endl;
}

void PrintAllRegs()
{
    PrintReg(R1);
    PrintReg(R2);
    PrintReg(R3);
    PrintReg(R4);
}

void PrintAllMem()
{
    for(size_t i=0; i<MAX_MEM; i++)
        PrintMem(MemoryAddressBuilder(i));
}

void PrintAll()
{
    PrintAllRegs();
    PrintAllMem();
}

void _InitMem(const Value init[32])
{
    for(size_t i=0; i<MAX_MEM; i++)
        MemoryContent[i] = init[i];
}

